/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using DataModelException = System.Qizx.Api.DataModelException;
using Node = System.Qizx.Api.Node;
using FullTextFactory = System.Qizx.Api.FullText.FullTextFactory;
using Scorer = System.Qizx.Api.FullText.Scorer;
using FONINode = System.Qizx.Xdm.FONIDataModel.FONINode;
namespace System.Qizx.Queries.Iterators
{
	
	/// <summary> General container iterator.
	/// <p>
	/// Enumerates nodes of the 'container' iterator that contain at least one node
	/// of the contained iterator.
	/// <p>Replaces both the Ancestor and the Fulltext Container iterators.
	/// <p>The 'predicate' iterator can typically be a boolean predicate 
	/// represented by iterators All, Any, Occurs etc.
	/// Clauses:<ul>
	/// <li>Basic iterator: any kind
	/// <li>Or, And, Then (ordered And), Not
	/// <li>MildNot (not in) is FT specific
	/// <li>Repeat: from M to N on an iterator
	/// <li>Depth constraint: manage Child/Descendant
	/// </ul>
	/// </summary>
	public class ContainerIterator:PostingIteratorBase
	{
		virtual public PostingIterator Ignored
		{
			set
			{
				ignored = value;
			}
			
		}
		public override int NodeSpan
		{
			get
			{
				return containing.NodeSpan;
			}
			
		}
		public override int Depth
		{
			get
			{
				return containing.Depth;
			}
			
		}
		/// <summary> Called when used for Full-text: attaches an elementary Scorer to
		/// each sub-iterator. 
		/// The scorers are normalized here.
		/// </summary>
		virtual public FullTextFactory Scoring
		{
			set
			{
				this.scoringFactory = value;
			}
			
		}
		private PostingIterator containing;
		private PostingIterator contained;
		private PostingIterator ignored;
		
		// When used for full-text: info for scoring
		private FullTextFactory scoringFactory;
		private ScoringInfo scoringInfo;
		
		protected internal class ScoringInfo
		{
			private PostingIterator ftIterator;
			private double currentScore;
			internal Scorer scorer;
			
			public ScoringInfo(PostingIterator ft)
			{
				ftIterator = ft;
				// brutal normalization: weight of top FT expression forced to 1
				ft.Weight = 1;
			}
			
			public virtual void  init(FullTextFactory scoringFactory)
			{
				scorer = scoringFactory.createScorer();
				double w = ftIterator.computeWeighting(scorer);
			}
		}
		
		public ContainerIterator(PostingIterator containing, PostingIterator contained)
		{
			this.containing = containing;
			this.contained = contained;
		}
		
		public override PostingIterator bornAgain()
		{
			ContainerIterator clone = new ContainerIterator(containing.bornAgain(), contained.bornAgain());
			if (ignored != null)
				clone.Ignored = ignored.bornAgain();
			if (scoringFactory != null)
				clone.Scoring = scoringFactory;
			return copyFilters(clone);
		}
		
		public override bool skipToDoc(int docId)
		{
			if (scoringInfo != null && scoringInfo.scorer == null)
			{
				scoringInfo.init(scoringFactory);
			}
			
			for (; ; )
			{
				if (!containing.skipToDoc(docId))
					return noMoreDocs();
				int cdoc = containing.DocId;
				if (!contained.skipToDoc(cdoc))
					return noMoreDocs();
				int idoc = contained.DocId;
				if (cdoc == idoc)
				{
					if (cdoc != curDocId)
					{
						changeDoc(cdoc);
					}
					return true;
				}
				docId = System.Math.Max(cdoc, idoc);
			}
		}
		
		protected internal override bool basicSkipToNode(int nodeId, int limit)
		{
			for (; ; )
			{
				if (!containing.skipToNode(nodeId, limit))
					return noMoreNodes(limit);
				int cstart = containing.NodeId;
				int cend = containing.NodeEnd;
				
				if (cstart < containing.PrevNodeEnd)
				{
					contained.resetToNode(cstart);
				}
				if (contained.inRange(cstart, cend))
				{
					curNodeId = cstart;
					return true;
				}
				// failed: force container to move
				nodeId = cstart + 1;
				// TODO? how to progress more quickly ?
				// method contained.getNextStart() ?
			}
		}
		
		public override void  resetToNode(int nodeId)
		{
			containing.resetToNode(nodeId);
			curNodeId = containing.NodeId;
		}
		
		// ----------------------------------------------------------------------
		
		public override void  initContainer(ContainerIterator cont, FullTextFactory scoringFactory)
		{
			base.initContainer(cont, scoringFactory);
			this.scoringFactory = scoringFactory;
			
			containing.initContainer(cont, scoringFactory);
			// propagate down: attention! not same container
			contained.initContainer(this, scoringFactory);
		}
		
		/// <summary> Called for each word actually used in the query. Not called for words
		/// excluded by full-text expressions 'ftnot' and 'not in'.
		/// </summary>
		/// <param name="word">
		/// </param>
		/// <param name="termFrequency">
		/// </param>
		/// <param name="inverseDocFrequency">
		/// </param>
		/// <param name="wordBoost">
		/// </param>
		internal virtual void  includedWord(char[] word, double termFrequency, double inverseDocFrequency, double wordBoost)
		{
		}
		
		/// <summary> Called back when defining the container with #initContainer.
		/// Tells the container to manage fulltext scoring
		/// </summary>
		public virtual void  addFulltextQuery(PostingIterator ft)
		{
			if (scoringInfo == null)
			{
				scoringInfo = new ScoringInfo(ft);
			}
		}
		
		public override double getFulltextScore(Node node)
		{
			return - 1;
		}
	}
}